﻿using System;
using System.Diagnostics.Contracts;

namespace YasharEl.Infrastructure.ObjectFactory
{
    public delegate TResult FactoryMethod<TResult>(IDependencyResolver resolver, Type requestedType, string key);
    public delegate object FactoryMethod(Type genericType, IDependencyResolver resolver, Type requestedType, string key);

    [ContractClass(typeof(DependencyRegistrarContract))]
    public interface IDependencyRegistrar
    {
        IDependencyRegistrar Register<TFrom, TTo>() where TTo : TFrom;
        IDependencyRegistrar Register<TFrom, TTo>(string key) where TTo : TFrom;
        IDependencyRegistrar Register<TFrom, TTo>(bool singleton) where TTo : TFrom;
        IDependencyRegistrar Register<TFrom, TTo>(string key, bool singleton) where TTo : TFrom;

        IDependencyRegistrar Register(Type fromType, Type toType, params object[] parameterValues);
        IDependencyRegistrar Register(Type fromType, Type toType, string key);
        IDependencyRegistrar Register(Type fromType, Type toType, bool singleton, params object[] parameterValues);
        IDependencyRegistrar Register(Type fromType, Type toType, string key, bool singleton);

        IDependencyRegistrar Register<TFrom>(TFrom instance);
        IDependencyRegistrar Register<TFrom>(TFrom instance, string key);

        IDependencyRegistrar Register<TFrom>(TFrom instance, bool singleton);
        IDependencyRegistrar Register<TFrom>(TFrom instance, string key, bool singleton);

        IDependencyRegistrar Register(Type type, object instance);
        IDependencyRegistrar Register(Type type, object instance, string key);

        IDependencyRegistrar RegisterFactory<TService>(FactoryMethod<TService> factoryMethod);
        IDependencyRegistrar RegisterFactory<TService>(FactoryMethod<TService> factoryMethod, DependencyLifeStyle dependencyLifeStyle, string moduleName = null);
        IDependencyRegistrar RegisterFactory(Type genericType, FactoryMethod factoryMethod);
        IDependencyRegistrar RegisterFactory(Type genericType, string key, FactoryMethod factoryMethod);
        IDependencyRegistrar RegisterFactory(Type genericType, FactoryMethod factoryMethod, DependencyLifeStyle dependencyLifeStyle, string moduleName = null);


        bool IsTypeRegistered(Type type, string key);
        bool IsTypeRegistered(Type type);

        bool IsTypeRegistered<TService>(string key);
        bool IsTypeRegistered<TService>();
    }

    [ContractClassFor(typeof(IDependencyRegistrar))]
    internal abstract class DependencyRegistrarContract : IDependencyRegistrar
    {
        public IDependencyRegistrar Register<TFrom, TTo>() where TTo : TFrom
        {
            return this;
        }

        public IDependencyRegistrar Register<TFrom, TTo>(string key) where TTo : TFrom
        {
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(key), "Key could not be null.");
            return this;
        }

        public IDependencyRegistrar Register(Type fromType, Type toType, params object[] parameterValues)
        {
            Contract.Requires<ArgumentNullException>(fromType != null, "fromType could not be null.");
            Contract.Requires<ArgumentNullException>(toType != null, "toType could not be null.");
            return this;
        }

        public IDependencyRegistrar Register(Type fromType, Type toType, string key)
        {
            Contract.Requires<ArgumentNullException>(fromType != null, "fromType could not be null.");
            Contract.Requires<ArgumentNullException>(toType != null, "toType could not be null.");
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(key), "Key could not be null.");
            return this;
        }

        public IDependencyRegistrar Register<TFrom>(TFrom instance)
        {
            Contract.Requires<ArgumentNullException>(instance != null, "instance could not be null.");
            return this;
        }

        public IDependencyRegistrar Register<TFrom>(TFrom instance, string key)
        {
            Contract.Requires<ArgumentNullException>(instance != null, "instance could not be null.");
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(key), "Key could not be null.");
            return this;
        }

        public IDependencyRegistrar Register(Type type, object instance)
        {
            Contract.Requires<ArgumentNullException>(type != null, "type could not be null.");
            Contract.Requires<ArgumentNullException>(instance != null, "instance could not be null.");
            return this;
        }

        public IDependencyRegistrar Register(Type type, object instance, string key)
        {
            Contract.Requires<ArgumentNullException>(type != null, "type could not be null.");
            Contract.Requires<ArgumentNullException>(instance != null, "instance could not be null.");
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(key), "Key could not be null.");
            return this;
        }

        public bool IsTypeRegistered(Type type, string key)
        {
            Contract.Requires<ArgumentNullException>(type != null, "type could not be null.");
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(key), "Key could not be null.");
            return default(bool);
        }

        public bool IsTypeRegistered(Type type)
        {
            Contract.Requires<ArgumentNullException>(type != null, "type could not be null.");
            return default(bool);
        }

        public bool IsTypeRegistered<TService>(string key)
        {
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(key), "Key could not be null.");
            return default(bool);
        }

        public bool IsTypeRegistered<TService>()
        {
            return default(bool);
        }

        public IDependencyRegistrar Register<TFrom>(TFrom instance, bool singleton)
        {
            Contract.Requires<ArgumentNullException>(instance != null, "instance could not be null.");
            return this;
        }

        public IDependencyRegistrar Register<TFrom>(TFrom instance, string key, bool singleton)
        {
            Contract.Requires<ArgumentNullException>(instance != null, "instance could not be null.");
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(key), "Key could not be null.");
            return this;
        }


        public IDependencyRegistrar Register(Type fromType, Type toType, bool singleton, params object[] parameterValues)
        {
            Contract.Requires<ArgumentNullException>(fromType != null, "fromType could not be null.");
            Contract.Requires<ArgumentNullException>(toType != null, "toType could not be null.");
            return this;
        }

        public IDependencyRegistrar Register<TFrom, TTo>(bool singleton) where TTo : TFrom
        {
            return this;
        }

        public IDependencyRegistrar Register<TFrom, TTo>(string key, bool singleton) where TTo : TFrom
        {
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(key), "Key could not be null.");
            return this;
        }


        public IDependencyRegistrar Register(Type fromType, Type toType, string key, bool singleton)
        {
            Contract.Requires<ArgumentNullException>(fromType != null, "fromType could not be null.");
            Contract.Requires<ArgumentNullException>(toType != null, "toType could not be null.");
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(key), "Key could not be null.");
            return this;
        }

        public IDependencyRegistrar RegisterFactory<TService>(FactoryMethod<TService> factoryMethod)
        {
            Contract.Requires<ArgumentNullException>(factoryMethod != null, "factoryMethod could not be null.");
            return this;
        }


        public IDependencyRegistrar RegisterFactory(Type genericType, FactoryMethod factoryMethod)
        {
            Contract.Requires<ArgumentNullException>(genericType != null, "genericType could not be null.");
            Contract.Requires<ArgumentNullException>(factoryMethod != null, "factoryMethod could not be null.");
            return this;
        }


        public IDependencyRegistrar RegisterFactory<TService>(FactoryMethod<TService> factoryMethod, DependencyLifeStyle dependencyLifeStyle, string moduleName = null)
        {
            Contract.Requires<ArgumentNullException>(factoryMethod != null, "factoryMethod could not be null.");
            return this;
        }


        public IDependencyRegistrar RegisterFactory(Type genericType, FactoryMethod factoryMethod, DependencyLifeStyle dependencyLifeStyle, string moduleName = null)
        {
            Contract.Requires<ArgumentNullException>(genericType != null, "genericType could not be null.");
            Contract.Requires<ArgumentNullException>(factoryMethod != null, "factoryMethod could not be null.");
            return this;
        }


        public IDependencyRegistrar RegisterFactory(Type genericType, string key, FactoryMethod factoryMethod)
        {
            Contract.Requires<ArgumentNullException>(genericType != null, "genericType could not be null.");
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(key), "Key could not be null.");
            Contract.Requires<ArgumentNullException>(factoryMethod != null, "factoryMethod could not be null.");
            return this;
        }
    }
}
